export default class {
    constructor ($container, options={}) {
        this.$container = $container;

        this._debounce = 100;
        this._offset = 0;

        this._start = {};
        this._move = {};
        this._end = {};

        this._touch_start = 0;
        this._transing = false;
        this._slient = false;

        this._scroll_top = 0;
        this._height = 0;
        this._queue = {};

        this._bind_touch();
    }

    _get_transform_y () {
        const transformY = window.getComputedStyle(this.$container)['transform'].match(/[-\d\.*\d*]+/g);
        return parseFloat(transformY? transformY[5] : 0);
    }

    reset () {
        this._scroll_top = 0;
        this._height = 0;
        this._init_container();
    }

    top () {
        this._offset = 0;
        this._set_transform();
    }

    cancel () {
        if (this._cancel_event) {
            this._cancel_event();
        }

        this.$container.parentNode.scrollTop = -this._offset;
        this._queue = {};
        this._offset = 0;
        this.$container.style.transition = 'none'
        this._set_transform()
    }

    _set_transform (offset) {
        this.$container.style.transform = `translate3d(0, ${offset || this._offset}px, 0)`;
    }

    _init_container () {
        if (!this._scroll_top || !this._height) {
            this._scroll_top = this.$container.parentNode.scrollHeight - this.$container.parentNode.offsetHeight;
            this._height = this.$container.parentNode.offsetHeight;
        }
    }

    _bind_touch() {
        const touchstartHandler = (e) => {
            this._start.x = e.changedTouches[0].pageX;
            this._start.y = e.changedTouches[0].pageY;
            this._touch_start = e.timeStamp;

            this._init_container();

            // 如果上一个动画还在执行过程中，此时重新点击一下之后，会造成动画直接完成。
            // 这样会造成界面瞬间滑动，此时获取实时的唯一值，设置偏移值为当前位移值
            if (this._transing) {
                this._transing = false;

                this._trigger('scroll');
                this._offset = this._get_transform_y()
                this._set_transform();
            }

            this.$container.style.transition = 'none';
        }

        const touchmoveHandler = (e) => {
            this._move.x = e.changedTouches[0].pageX;
            this._move.y = e.changedTouches[0].pageY;

            const x_distance = this._move.x - this._start.x;
            const y_distance = this._move.y - this._start.y;

            if (Math.abs(y_distance) > Math.abs(x_distance)) {
                console.log('offset', y_distance, this._offset, this._scroll_top)
                if (y_distance + this._offset > 0) {
                    this._set_transform(this._offset + y_distance * .2);
                } else if (y_distance + this._offset <= -this._scroll_top) {
                    this._set_transform(this._offset + y_distance * .2);
                } else {
                    this._set_transform(this._offset + y_distance);
                }
                e.preventDefault();
            }
        }

        const touchendHandler = (e) => {
            this._end.x = e.changedTouches[0].pageX;
            this._end.y = e.changedTouches[0].pageY;

            const x_distance = this._end.x - this._start.x;
            const y_distance = this._end.y - this._start.y;


            this._start = {}
            this._move = {}
            this._end = {}

            if (Math.abs(y_distance) > Math.abs(x_distance)) {
                this._offset += y_distance;

                const interval_time = e.timeStamp - this._touch_start;
                this._show_animation(interval_time, y_distance);

                e.preventDefault();
            }
        }

        const transitionEndHandler = (e) => {
            if (e.target !== this.$container) {
                return false;
            }

            this._transing = false;
            this._trigger('scroll');

            e.preventDefault();
        }

        this._cancel_event = () => {
            this.$container.removeEventListener('touchstart', touchstartHandler, false);
            this.$container.removeEventListener('touchmove', touchmoveHandler, false);
            this.$container.removeEventListener('touchend', touchendHandler, false);
            this.$container.removeEventListener('transitionend', transitionEndHandler, false);
        }

        this._bind_event = () => {
            this.$container.addEventListener('touchstart', touchstartHandler, false);
            this.$container.addEventListener('touchmove', touchmoveHandler, false);
            this.$container.addEventListener('touchend', touchendHandler, false);
            this.$container.addEventListener('transitionend', transitionEndHandler, false);
        }

        this._bind_event();
    }

    _show_animation (interval, offset) {
        this._transing = true;

        const compute_scroll = (speed, distance) => {
            const MAX_SPEED = 2;
            const SCROLL_ACCELERATION = 0.001;

            const temp_speed = speed > MAX_SPEED ? MAX_SPEED : speed < -MAX_SPEED? -MAX_SPEED: speed;
            const acceleration = SCROLL_ACCELERATION * (temp_speed >= 0? 1 : -1);

            let interval = temp_speed / acceleration;
            let offset = interval * temp_speed / 2;

            if (this._offset + this._scroll_top < -100 && distance < -300) {
                this._trigger('bottom');
            }

            if (this._offset + offset > 0) {
                offset = -this._offset;
                interval = interval * .2;
            }

            if (this._offset + offset < -this._scroll_top) {
                offset = -this._scroll_top - this._offset;
                interval = interval * .2;
            }

            return { interval, offset }
        }

        const res = compute_scroll(offset / interval, offset)

        this._offset += res.offset;

        this._set_transform();
        this.$container.style.transition = `${res.interval}ms ease-out`
    }

    _trigger (action) {
        if (this._slient) {
            this._slient = false;
            return;
        }

        if (!this._queue[action]) return;

        for (let func of this._queue[action]) {
            func.call(null);
        }
    }

    listen (func, event='scroll') {
        if (!this._queue[event]) this._queue[event] = [];

        this._queue[event].push(func);
    }

    scrollIntoView ($el, slient=false) {
        this._init_container();

        this._slient = slient;

        const max_offset = $el.offsetTop - this._height / 4;
        const min_offset = $el.offsetTop - this._height / 4 * 3;

        if (-this._offset > max_offset && -this._offset < min_offset) return;
        let temp_offset = max_offset;

        if ($el.offsetHeight < this._height / 4) {
            if (-this._offset < max_offset) {
                temp_offset = min_offset
            }
        }

        if (temp_offset < 0) {
            temp_offset = 0;
        } else if (temp_offset > this._scroll_top) {
            temp_offset = this._scroll_top;
        }

        this._offset = -temp_offset;

        this._set_transform()
        this.$container.style.transition = '500ms ease-out'
    }
}
